package com.ltx.vizflowservice.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ltx.vizflowservice.common.convention.errorcode.BaseErrorCode;
import com.ltx.vizflowservice.common.convention.result.Result;
import com.ltx.vizflowservice.common.convention.result.Results;
import com.ltx.vizflowservice.dao.UserMapper;
import com.ltx.vizflowservice.domain.dto.UserDTO;
import com.ltx.vizflowservice.domain.entity.User;
import com.ltx.vizflowservice.domain.vo.UserVO;
import com.ltx.vizflowservice.service.UserService;
import com.ltx.vizflowservice.util.JwtUtil;
import com.ltx.vizflowservice.util.UserContext;
import com.ltx.vizflowservice.util.ValidatorUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;

import static com.ltx.vizflowservice.util.AESUtil.encrypt;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author 罗添煦
 * @since 2024-11-15
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    // Redis操作模板
    private final StringRedisTemplate redisTemplate;

    @Override
    public Result<?> login(UserDTO userDTO) throws Exception {
        // 查询用户
        String username = userDTO.getUsername();
        LambdaQueryWrapper<User> queryByUserName = Wrappers.lambdaQuery(User.class)
                .eq(User::getUserName, username);
        User user = baseMapper.selectOne(queryByUserName);
        // 用户不存在
        if (user == null){
            return Results.failure(BaseErrorCode.USER_INFO_NULL);
        }else if (!user.getPassword().equals(encrypt(userDTO.getPassword()))){
            return Results.failure(BaseErrorCode.USER_PASSWORD_ERROR);
        }else {
            // 生成token
            String token = JwtUtil.generateToken(user.getUserId());
            // 缓存token
            redisTemplate.opsForValue().set(token, user.getUserId(), 7, TimeUnit.DAYS);
            log.info("用户{}登录成功，token为{}", username, token);
            return Results.success(new UserVO(token,StringUtils.hasLength(user.getUserPhone())));
        }
    }

    @Override
    public Result<?> register(UserDTO userDTO) throws Exception {
        // 查询用户名是否已存在
        LambdaQueryWrapper<User> queryByUserName = Wrappers.lambdaQuery(User.class)
                .eq(User::getUserName, userDTO.getUsername());
        User user = baseMapper.selectOne(queryByUserName);
        // 用户名已存在
        if (user != null){
            return Results.failure(BaseErrorCode.USER_NAME_EXIST_ERROR);
        }
        // 密码不一致
        if (!userDTO.getPassword().equals(userDTO.getConfirmPassword())){
            return Results.failure(BaseErrorCode.PASSWORD_NOT_EQUAL_ERROR);
        }
        // 新建用户
        User registerUser = new User();
        registerUser.setUserName(userDTO.getUsername());
        registerUser.setPassword(encrypt(userDTO.getPassword()));
        baseMapper.insert(registerUser);
        return Results.success();
    }

    @Override
    public Result<?> send(String phone) {
        boolean phoneNumberValid = ValidatorUtils.isPhoneNumberValid(phone);
        if (!phoneNumberValid){
            return Results.failure(BaseErrorCode.PHONE_VERIFY_ERROR);
        }
        // 三分钟内不空重复发送验证码
        String s = redisTemplate.opsForValue().get(phone);
        if (StringUtils.hasLength(s)){
            return Results.failure(BaseErrorCode.PHONE_CODE_ERROR);
        }
        RandomGeneratorFactory<RandomGenerator> factory = RandomGeneratorFactory.of("Xoroshiro128PlusPlus");
        int code = factory.create().nextInt(99999, 999999);
        redisTemplate.opsForValue().set(phone, String.valueOf(code), 3, TimeUnit.MINUTES);
        log.info("发送验证码{}", code);
        return Results.success(code);
    }

    @Override
    public Result<?> resetPassword(UserDTO userDTO) throws Exception {
        // 校验手机号
        String phone = userDTO.getPhone();
        boolean phoneNumberValid = ValidatorUtils.isPhoneNumberValid(phone);
        if (!phoneNumberValid){
            return Results.failure(BaseErrorCode.PHONE_VERIFY_ERROR);
        }
        String s = redisTemplate.opsForValue().get(phone);
        // 校验验证码
        if (Objects.equals(s, userDTO.getCode())){
            // 查询用户
            LambdaQueryWrapper<User> queryByPhone = Wrappers.lambdaQuery(User.class)
                    .eq(User::getUserPhone, phone);
            User user = baseMapper.selectOne(queryByPhone);
            if (user == null){
                return Results.failure(BaseErrorCode.USER_INFO_NULL);
            }
            // 更新密码
            user.setPassword(encrypt(userDTO.getNewPassword()));
            baseMapper.updateById(user);
        }else {
            return Results.failure(BaseErrorCode.CODE_ERROR);
        }
        return Results.success();
    }

    @Override
    public Result<?> bindPhone(UserDTO userDTO) throws Exception {
        // 获取用户
        User user = UserContext.getUser();
        // 校验手机号
        String phone = userDTO.getPhone();
        boolean phoneNumberValid = ValidatorUtils.isPhoneNumberValid(phone);
        if (!phoneNumberValid){
            return Results.failure(BaseErrorCode.PHONE_VERIFY_ERROR);
        }
        String s = redisTemplate.opsForValue().get(phone);
        if (Objects.equals(s, userDTO.getCode())){
            // 查询用户
            String username = userDTO.getUsername();
            LambdaQueryWrapper<User> queryByUserName = Wrappers.lambdaQuery(User.class)
                    .eq(User::getUserName, username);
            // 绑定手机号
            user.setUserPhone(encrypt(phone));
            baseMapper.updateById(user);
        }else {
            return Results.failure(BaseErrorCode.CODE_ERROR);
        }
        return Results.success();
    }
}
